﻿using System;
using System.Collections.Generic;
using System.Text;
using HenIT.Utilities;
using System.IO;
using btom = Microsoft.BizTalk.ExplorerOM;
using System.Diagnostics;

namespace HenIT.BizTalk
{
    class Program
    {
        private static string logFile = Path.Combine(Environment.CurrentDirectory, "BTSBackup.log");
        private static readonly string[] btsSTDInstallDirectories = new string[]
            {
                @"Program Files\Microsoft BizTalk Server 2013 R2",
                @"Program Files (x86)\Microsoft BizTalk Server 2013 R2",
                @"Program Files\Microsoft BizTalk Server 2013",
                @"Program Files (x86)\Microsoft BizTalk Server 2013",
                @"Program Files\Microsoft BizTalk Server 2010",
                @"Program Files (x86)\Microsoft BizTalk Server 2010",
                @"Program Files\Microsoft BizTalk Server 2009",
                @"Program Files (x86)\Microsoft BizTalk Server 2009",
                @"Program Files\Microsoft BizTalk Server 2006",
                @"Program Files (x86)\Microsoft BizTalk Server 2006"
            };
        private static List<string> btsInstallDirectories = new List<string>();
        private static bool logToFile = false;
        

        static void Main(string[] args)
        {
            if (args.Length == 0 || CommandLineUtils.IsCommand(args, "-?", "/?", "-h", "/h"))
            {
                ShowHelp();
            }
            else
            {
                logToFile = CommandLineUtils.IsCommand(args, "-log", "/log");
                string mgmtSrv = BizTalkUtils.GetCurrentBizTalkSqlServer();
                string mgmtDb = BizTalkUtils.GetCurrentBizTalkMgmtDb();
                btsInstallDirectories.AddRange(btsSTDInstallDirectories);
                string btsInstallDir = CommandLineUtils.GetCommand(args, "", "-btsdir:", "/btsdir:");
                if (btsInstallDir.Length > 0)
                {
                    if (!btsInstallDirectories.Contains(btsInstallDir))
                        btsInstallDirectories.Add(btsInstallDir);
                }                

                #region Back up BTSNTSvc config files (WMI)
                string configBackupPath = CommandLineUtils.GetCommand(args, "", "-config:", "/config:");
                if (configBackupPath.Length > 0)
                {
                    configBackupPath = FormatMacros(configBackupPath);
                    WriteLog("Backing up BTSNTSvc.exe config and backup files");
                    foreach (string server in BTSWMITools.GetGroupServerNames())
                    {
                        try
                        {
                            WriteLog("  Backing up config files from " + server);
                            BackupBTSConfigFiles(server, configBackupPath);
                        }
                        catch (Exception configEx)
                        {
                            WriteLog(configEx.ToString());
                        }
                    }
                }
                #endregion

                #region Component backups 
                string componentBackupPath = CommandLineUtils.GetCommand(args, "", "-comp:", "/comp:");
                if (componentBackupPath.Length > 0)
                {
                    componentBackupPath = FormatMacros(componentBackupPath);
                    WriteLog("Backing up messaging component files (local machine only)");
                    BackupBTSMsgComponentFiles(componentBackupPath);

                    WriteLog("Backing up pipeline component files (local machine only)");
                    BackupBTSPipelineComponentFiles(componentBackupPath);
                }
                #endregion

                #region Create environment config file (WMI)
                string envconfigPath = CommandLineUtils.GetCommand(args, "", "-envconfig:", "/envconfig:");
                if (envconfigPath.Length > 0)
                {
                    envconfigPath = FormatMacros(envconfigPath);
                    WriteLog("Creating environment config file");
                    try
                    {
                        CreateEnvConfigFile(envconfigPath);
                    }
                    catch (Exception envConfigEx)
                    {
                        WriteLog(envConfigEx.ToString());
                    }
                } 
                #endregion

                #region Exporting MSIs and binding files (ExplorerOM)
                string appsExportPath = CommandLineUtils.GetCommand(args, "", "-apps:", "/apps:");
                string bindingsExportPath = CommandLineUtils.GetCommand(args, "", "-bind:", "/bind:");
                if (appsExportPath.Length > 0 || bindingsExportPath.Length > 0)
                {
                    string btsTaskEXE = GetBTSTaskPath();
                    if (btsTaskEXE.Length > 0)
                    {
                        #region Export application MSI files
                        if (appsExportPath.Length > 0)
                        {

                            appsExportPath = FormatMacros(appsExportPath);
                            WriteLog("Exporting all application MSI files");
                            ExportApps(mgmtSrv, mgmtDb, appsExportPath, btsTaskEXE);

                        }
                        #endregion

                        #region Export application binding files
                        if (bindingsExportPath.Length > 0)
                        {
                            bindingsExportPath = FormatMacros(bindingsExportPath);
                            WriteLog("Exporting all application binding files");
                            ExportBindings(mgmtSrv, mgmtDb, bindingsExportPath, btsTaskEXE);

                        }
                        #endregion
                    }
                } 
                #endregion

                #region Back up tracking/BAM
                string trackingBackupPath = CommandLineUtils.GetCommand(args, "", "-track:", "/track:");
                if (trackingBackupPath.Length > 0)
                {
                    trackingBackupPath = FormatMacros(trackingBackupPath);
                    WriteLog("Backing up custom tracking files (local machine only)");
                    BackupBTSTrackingFiles(trackingBackupPath);
                }
                #endregion

                WriteLog(new string('-', 30));
            }
            
#if DEBUG
            Console.WriteLine("Done");
            System.Threading.Thread.Sleep(5000);
#endif
        }

        #region Back up config files (and BizTalk2006ConfigurationBackup.xml if exist)
        /// <summary>
        /// Back up config files
        /// </summary>
        /// <param name="server">Name of the BizTalk Server (instance)</param>
        /// <param name="sourceDirectory">Source directory - e.g. Program Files\Microsoft BizTalk Server 2006</param>
        /// <param name="backupPath">Path to back up files to</param>
        private static void BackupBTSConfigFiles(string server, string backupPath)
        {
            int filesCopied = 0;
            if (!Directory.Exists(backupPath))
            {
                WriteLog("    Creating directory " + backupPath);
                Directory.CreateDirectory(backupPath);
            }
            foreach (string btsInstallDirectory in btsInstallDirectories)
            {
                string directoryPath = @"\\" + server + @"\" + Properties.Settings.Default.BTSInstallDriveLetter + @"$\" + btsInstallDirectory;
                if (Directory.Exists(directoryPath))
                {
                    filesCopied = 0;
                    WriteLog("    Directory " + directoryPath);
                    //BizTalk service exacutable config
                    string[] files = Directory.GetFiles(directoryPath, "BTSNTSvc*.exe.config");
                    foreach (string configFilePath in files)
                    {
                        string fileName = Path.GetFileName(configFilePath);
                        WriteLog("      " + fileName);
                        File.Copy(configFilePath, Path.Combine(backupPath, server + fileName), true);
                        filesCopied++;
                    }
                    // ConfigurationBackup file
                    files = Directory.GetFiles(directoryPath, "BizTalk*ConfigurationBackup.xml");
                    foreach (string configFilePath in files)
                    {
                        string fileName = Path.GetFileName(configFilePath);
                        WriteLog("      " + fileName);
                        File.Copy(configFilePath, Path.Combine(backupPath, server + fileName), true);
                        filesCopied++;
                    }
                    WriteLog("      " + filesCopied.ToString() + " file(s) copied");
                }
            }            
        }
        #endregion

        #region Back up Component files
        private static void BackupBTSMsgComponentFiles(string componentBackupPath)
        {
            int filesCopied = 0;
            string msgComponentBackupPath = Path.Combine(componentBackupPath, "Messaging Components");
            if (!Directory.Exists(msgComponentBackupPath))
            {
                WriteLog("  Creating directory " + msgComponentBackupPath);
                Directory.CreateDirectory(msgComponentBackupPath);
            }
            foreach (string btsInstallDirectory in btsInstallDirectories)
            {
                string directoryPath = Path.Combine(Path.Combine(Properties.Settings.Default.BTSInstallDriveLetter + @":\", btsInstallDirectory), "Messaging Components");
                if (Directory.Exists(directoryPath))
                {
                    filesCopied = 0;
                    WriteLog("  Directory " + directoryPath);
                    string[] files = Directory.GetFiles(directoryPath);
                    foreach (string file in files)
                    {
                        string fileName = Path.GetFileName(file);
                        if (!fileName.StartsWith("Microsoft.BizTalk"))
                        {
                            WriteLog("    " + fileName);
                            File.Copy(file, Path.Combine(msgComponentBackupPath, fileName), true);
                            filesCopied++;
                        }
                    }
                    WriteLog("    " + filesCopied.ToString() + " file(s) copied");
                }
            }
        }
        private static void BackupBTSPipelineComponentFiles(string componentBackupPath)
        {
            int filesCopied = 0;
            string msgComponentBackupPath = Path.Combine(componentBackupPath, "Pipeline Components");
            if (!Directory.Exists(msgComponentBackupPath))
            {
                WriteLog("  Creating directory " + msgComponentBackupPath);
                Directory.CreateDirectory(msgComponentBackupPath);
            }
            foreach (string btsInstallDirectory in btsInstallDirectories)
            {
                string directoryPath = Path.Combine(Path.Combine(Properties.Settings.Default.BTSInstallDriveLetter + @":\", btsInstallDirectory), "Pipeline Components");
                if (Directory.Exists(directoryPath))
                {
                    filesCopied = 0;
                    WriteLog("  Directory " + directoryPath);
                    string[] files = Directory.GetFiles(directoryPath);
                    foreach (string file in files)
                    {
                        string fileName = Path.GetFileName(file);
                        if (!fileName.StartsWith("Microsoft.BizTalk"))
                        {
                            WriteLog("    " + fileName);
                            File.Copy(file, Path.Combine(msgComponentBackupPath, fileName), true);
                            filesCopied++;
                        }
                    }
                    WriteLog("    " + filesCopied.ToString() + " file(s) copied");
                }
            }
        }
        #endregion

        #region Create environment config file
        private static void CreateEnvConfigFile(string envconfigPath)
        {
            string environmentConfigFilePath = Path.Combine(envconfigPath, "BtsAdminConfiguration.xml");
            Directory.CreateDirectory(envconfigPath);
            FileStream outstream = File.Create(environmentConfigFilePath);
            using (System.Xml.XmlTextWriter wrt = new System.Xml.XmlTextWriter(outstream, Encoding.UTF8))
            {
                wrt.Formatting = System.Xml.Formatting.Indented;
                wrt.Indentation = 5;
                wrt.IndentChar = ' ';
                wrt.WriteStartDocument();
                wrt.WriteStartElement("BtsAdminConfiguration");

                #region Hosts
                wrt.WriteStartElement("Hosts");
                WriteHostConfigEntries(wrt);
                wrt.WriteEndElement(); 
                #endregion

                #region HostInstances
                wrt.WriteStartElement("HostInstances");
                WriteHostInstanceEntries(wrt);
                wrt.WriteEndElement();
                #endregion

                #region Adapters
                wrt.WriteStartElement("Adapters");
                WriteAdapterEntries(wrt);
                wrt.WriteEndElement();
                #endregion

                wrt.WriteEndElement();
                wrt.WriteEndDocument();
                wrt.Flush();
            }
            outstream.Close();
        }

        private static void WriteHostConfigEntries(System.Xml.XmlTextWriter wrt)
        {
            foreach (BizTalkHost bizTalkHost in BTSWMITools.GetBizTalkHosts())
            {
                wrt.WriteStartElement("Host");
                wrt.WriteAttributeString("hostname", bizTalkHost.HostName);
                wrt.WriteAttributeString("ntgroupname", bizTalkHost.NTGroupName);
                wrt.WriteAttributeString("isdefault", bizTalkHost.IsDefault.ToString());
                wrt.WriteAttributeString("hosttracking", bizTalkHost.HostTracking.ToString());
                wrt.WriteAttributeString("authtrusted", bizTalkHost.AuthTrusted.ToString());
                wrt.WriteAttributeString("hosttype", bizTalkHost.HostType.ToString());
                wrt.WriteAttributeString("ishost32bitonly", bizTalkHost.Is32BitOnly.ToString()); 
                wrt.WriteEndElement(); 
            }
        }

        private static void WriteHostInstanceEntries(System.Xml.XmlTextWriter wrt)
        {
            foreach (BizTalkHostInstance bizTalkHostInstance in BTSWMITools.GetBizTalkHostInstances())
            {
                wrt.WriteStartElement("HostInstance");
                wrt.WriteAttributeString("servername", bizTalkHostInstance.ServerName);
                wrt.WriteAttributeString("hostname", bizTalkHostInstance.HostName);
                wrt.WriteAttributeString("username", bizTalkHostInstance.Logon);
                wrt.WriteAttributeString("password", "");
                wrt.WriteAttributeString("startinstance", "true");
                wrt.WriteAttributeString("isdisabled", bizTalkHostInstance.IsDisabled.ToString());
                wrt.WriteAttributeString("servicestate", bizTalkHostInstance.ServiceState.ToString());
                wrt.WriteEndElement(); 
            }
        }

        private static void WriteAdapterEntries(System.Xml.XmlTextWriter wrt)
        {
            List<BizTalkAdapterHandler> receiveHandlers = BTSWMITools.GetBizTalkReceiveHandlers();
            List<BizTalkAdapterHandler> sendHandlers = BTSWMITools.GetBizTalkSendHandlers();

            foreach (BizTalkAdapter bizTalkAdapter in BTSWMITools.GetBizTalkAdapters())
            {
                wrt.WriteStartElement("Adapter");
                wrt.WriteAttributeString("name", bizTalkAdapter.Name);
                wrt.WriteAttributeString("type", bizTalkAdapter.Type);
                wrt.WriteAttributeString("comment", bizTalkAdapter.Comment);

                foreach (BizTalkAdapterHandler handler in receiveHandlers)
                {
                    if (handler.Adapter == bizTalkAdapter.Name)
                    {
                        wrt.WriteStartElement("ReceiveHandler");
                        wrt.WriteAttributeString("hostname", handler.HostName);
                        wrt.WriteEndElement();
                    }
                }

                foreach (BizTalkAdapterHandler handler in sendHandlers)
                {
                    if (handler.Adapter == bizTalkAdapter.Name)
                    {
                        wrt.WriteStartElement("SendHandler");
                        wrt.WriteAttributeString("hostname", handler.HostName);
                        wrt.WriteEndElement();
                    }
                }

                wrt.WriteEndElement();
            }
        }
        #endregion

        #region Export application MSI files
        /// <summary>
        /// Export all (non system) BizTalk applications
        /// </summary>
        /// <param name="mgmtSrv">BizTalk management database sql server</param>
        /// <param name="mgmtDb">BizTalk management database name</param>
        /// <param name="appsExportPath">Path to export files to</param>
        /// <param name="btsTaskEXE">Path to BTSTask.exe</param>
        private static void ExportApps(string mgmtSrv, string mgmtDb, string appsExportPath, string btsTaskEXE)
        {
            if (!Directory.Exists(appsExportPath))
            {
                WriteLog("  Creating directory " + appsExportPath);
                Directory.CreateDirectory(appsExportPath);
            }
            using (btom.BtsCatalogExplorer catalog = new btom.BtsCatalogExplorer())
            {
                catalog.ConnectionString = "SERVER=" + mgmtSrv + ";DATABASE=" + mgmtDb + ";Integrated Security=SSPI";
                foreach (btom.Application application in catalog.Applications)
                {
                    if (!application.IsSystem)
                    {
                        WriteLog("    Exporting MSI for " + application.Name);
                        string parameters = "ExportApp /ApplicationName:\"" + application.Name + "\" /Package:\"" + appsExportPath + "\\" + application.Name + ".msi\" /Server:" + mgmtSrv + " /Database:" + mgmtDb;
                        Process btsExportAppsProcess = Process.Start(new ProcessStartInfo(btsTaskEXE, parameters));
                        btsExportAppsProcess.WaitForExit();
                    }
                }
            }
        }
        #endregion

        #region Export application binding files
        /// <summary>
        /// Export all (non system) BizTalk application binding files
        /// </summary>
        /// <param name="mgmtSrv">BizTalk management database sql server</param>
        /// <param name="mgmtDb">BizTalk management database name</param>
        /// <param name="bindingsExportPath">Path to export files to</param>
        /// <param name="btsTaskEXE">Path to BTSTask.exe</param>
        private static void ExportBindings(string mgmtSrv, string mgmtDb, string bindingsExportPath, string btsTaskEXE)
        {
            if (!Directory.Exists(bindingsExportPath))
            {
                WriteLog("  Creating directory " + bindingsExportPath);
                Directory.CreateDirectory(bindingsExportPath);
            }
            using (btom.BtsCatalogExplorer catalog = new btom.BtsCatalogExplorer())
            {
                catalog.ConnectionString = "SERVER=" + mgmtSrv + ";DATABASE=" + mgmtDb + ";Integrated Security=SSPI";
                foreach (btom.Application application in catalog.Applications)
                {
                    if (!application.IsSystem)
                    {
                        WriteLog("    Exporting bindings for " + application.Name);
                        string parameters = "ExportBindings -ApplicationName:\"" + application.Name + "\" -Destination:\"" + bindingsExportPath + "\\" + application.Name + ".BindingInfo.xml\" /Server:" + mgmtSrv + " /Database:" + mgmtDb;
                        Process btsExportAppsProcess = Process.Start(new ProcessStartInfo(btsTaskEXE, parameters));
                        btsExportAppsProcess.WaitForExit();
                    }
                }
            }
        }
        #endregion

        #region Back up tracking files
        private static void BackupBTSTrackingFiles(string trackingBackupPath)
        {
            int filesCopied = 0;
            if (!Directory.Exists(trackingBackupPath))
            {
                WriteLog("  Creating directory " + trackingBackupPath);
                Directory.CreateDirectory(trackingBackupPath);
            }
            foreach (string btsInstallDirectory in btsInstallDirectories)
            {
                string directoryPath = Path.Combine(Path.Combine(Properties.Settings.Default.BTSInstallDriveLetter + @":\", btsInstallDirectory), "Tracking");
                if (Directory.Exists(directoryPath))
                {
                    filesCopied = 0;
                    WriteLog("  Directory " + directoryPath);
                    string[] files = Directory.GetFiles(directoryPath);
                    foreach (string file in files)
                    {
                        string fileName = Path.GetFileName(file);
                        bool canCopy = false;
                        foreach (string fileType in Properties.Settings.Default.TrackingFilesToBackup)
                        {
                            if (fileName.EndsWith(fileType, StringComparison.InvariantCultureIgnoreCase))
                            {
                                canCopy = true;
                                break;
                            }
                        }

                        if (canCopy)
                        {
                            WriteLog("    " + fileName);
                            File.Copy(file, Path.Combine(trackingBackupPath, fileName), true);
                            filesCopied++;
                        }
                    }
                    WriteLog("    " + filesCopied.ToString() + " file(s) copied");
                }
            }
        } 
        #endregion

        #region Helper functions
        private static string GetBTSTaskPath()
        {
            foreach (string btsInstallDirectory in btsInstallDirectories)
            {
                if (File.Exists(Path.Combine(Path.Combine(Properties.Settings.Default.BTSInstallDriveLetter + @":\", btsInstallDirectory), "BTSTask.exe")))
                    return Path.Combine(Path.Combine(Properties.Settings.Default.BTSInstallDriveLetter + @":\", btsInstallDirectory), "BTSTask.exe");
            }
            WriteLog("Could not locate BTSTask.exe!");
            return "";
        }

        private static string FormatMacros(string path)
        {
            if (path.Contains("{"))
                return path.Replace("{yyyy-MM-dd}", DateTime.Now.ToString("yyyy-MM-dd"))
                .Replace("{dd-MM-yyyy}", DateTime.Now.ToString("dd-MM-yyyy"))
                .Replace("{MM-dd-yyyy}", DateTime.Now.ToString("MM-dd-yyyy"))
                .Replace("{yyyyMMdd}", DateTime.Now.ToString("yyyyMMdd"))
                .Replace("{ddMMyyyy}", DateTime.Now.ToString("ddMMyyyy"))
                .Replace("{MMddyyyy}", DateTime.Now.ToString("MMddyyyy"));
            else
                return path;
        }

        private static void WriteLog(string message)
        {
            if (logToFile)
                File.AppendAllText(logFile, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":" + message + "\r\n");
            Console.WriteLine(message);
        }
        #endregion

        private static void ShowHelp()
        {
            Console.WriteLine("Usage: BTSBackup.exe [-config:path] [-comp:path] [-envconfig:path] [-apps:path] [-bind:path] [-track:path] [-log] [-btsdir:installdir]");
            Console.WriteLine("  Where");
            Console.WriteLine("    config    - Back up BTSNTSvc.exe.config files to path");
            Console.WriteLine("    comp      - Back up components (pipeline and messaging)");
            Console.WriteLine("    envconfig - Generate BtsAdminConfiguration.xml file to path");
            Console.WriteLine("    apps      - Export all application MSI's to path");
            Console.WriteLine("    bind      - Export all application binding files to path");
            Console.WriteLine("    track     - Back up custom tracking(BAM) related files to path");
            Console.WriteLine("    log       - Write outout to log file (BTSBackup.log)");
            Console.WriteLine("    btsdir    - Installation directory of BizTalk");
            Console.WriteLine("  Path details");
            Console.WriteLine("    path      - May contain macros like {yyyy-MM-dd}");
            Console.WriteLine("    Allowed macros");
            Console.WriteLine("      yyyy-MM-dd");
            Console.WriteLine("      dd-MM-yyyy");
            Console.WriteLine("      MM-dd-yyyy");
            Console.WriteLine("      yyyyMMdd");
            Console.WriteLine("      ddMMyyyy");
            Console.WriteLine("      MMddyyyy");
            Console.WriteLine(" Sample usage: BTSBackup.exe \"-config:c:\\backups\\{yyyy-MM-dd}\\config\" -log");
        }
    }
}
